﻿namespace NoIdeaIoc;
/// <summary>
/// Ioc容器的生命周期管理接口
/// </summary>
public class LifeTime : ILifeTime, IServiceScope
{
    /// <summary>
    /// 是否Ioc容器已经创建完毕
    /// </summary>
    public static bool IsIocBuilded => _staticRootIoc != null;
    static LifeTime _staticRootIoc;
    internal static void InitRoot(List<ServiceContainer> resolveInterfaces, List<IocResponse> responses)
    {
        var newIoc = new LifeTime(resolveInterfaces, responses);
        Interlocked.Exchange(ref _staticRootIoc, newIoc);
        var tempWaitReset = waitReset;
        if (tempWaitReset != null)
        {
            Interlocked.Exchange(ref waitReset, null);
            tempWaitReset.Set();
            tempWaitReset.Dispose();
        }
    }
    const int waitTimeout = 60000;
    static ManualResetEvent waitReset = new ManualResetEvent(false);
    /// <summary>
    /// 等待系统初始化完成
    /// </summary>
    public static void CheckSystemInited()
    {
        var tempWaitReset = waitReset;
        if (tempWaitReset != null)
        {
            //等待
            if (!tempWaitReset.WaitOne(waitTimeout, false))
            {
                throw new Exception($"等待{typeof(LifeTime).FullName}对象初始化超时");
            }
        }
    }
    /// <summary>
    /// Ioc容器的生命周期管理接口
    /// </summary>
    /// <param name="resolveInterfaces"></param>
    /// <param name="responses"></param>
    private LifeTime(List<ServiceContainer> resolveInterfaces, List<IocResponse> responses)
    {
        _stopwatch = Stopwatch.StartNew();
        _sessionId = Guid.NewGuid();
        this.resolveInterfaces = resolveInterfaces;
        disposables = new List<object>(5);
        responseds = responses;
        lockObj = ((System.Collections.ICollection)responseds).SyncRoot;
    }
    readonly object lockObj;
    LifeTime rootIoc;
    /// <summary>
    /// Ioc容器的生命周期管理接口
    /// </summary>
    public LifeTime()
    {
        CheckSystemInited();
        _stopwatch = Stopwatch.StartNew();
        _sessionId = Guid.NewGuid();
        rootIoc = _staticRootIoc;
        this.resolveInterfaces = _staticRootIoc.resolveInterfaces;
        disposables = new List<object>(5);
        responseds = new List<IocResponse>(5);
        lockObj = ((System.Collections.ICollection)responseds).SyncRoot;
    }
    readonly Stopwatch _stopwatch;
    /// <summary>
    /// 获取计时器
    /// </summary>
    public Stopwatch Stopwatch => _stopwatch;
    readonly List<ServiceContainer> resolveInterfaces;
    readonly List<object> disposables;
    /// <summary>
    /// 已获取的实例集合
    /// <para>对于根容器来说，就是全部都是单例</para>
    /// </summary>
    readonly List<IocResponse> responseds;
    Guid _sessionId;
    /// <summary>
    /// 会话Id
    /// </summary>
    public Guid SessionId => _sessionId;
    public T Create<T>() where T : new()
    {
        if (_isDisposed) throw new ObjectDisposedException(nameof(LifeTime), "无法访问已释放容器");
        var instance = new T();
        var cacheKey = ResolveUnitBase.GetCacheKey(typeof(T), null);
        var tempInstance = new IocResponse(cacheKey, instance, ServiceLifetime.Scoped);
        lock (lockObj)
        {
            responseds.Add(tempInstance);
        }
        return instance;
    }
    /// <summary>
    /// 将新对象注册为系统单例
    /// <para>可被容器获取</para>
    /// </summary>
    /// <param name="instance"></param>
    public void RegisterSingleton(object instance)
    {
        if (instance == null) return;
        var cacheKey = ResolveUnitBase.GetCacheKey(instance.GetType(), null);
        var tempInstance = instance as IocResponse;
        if (tempInstance == null) tempInstance = new IocResponse(cacheKey, instance, ServiceLifetime.Singleton);
        addToResolvedResponse(tempInstance);
    }
    /// <summary>
    /// 将新对象注册为本生命周期服务
    /// <para>可被容器获取</para>
    /// </summary>
    /// <param name="instance"></param>
    public void RegisterService(object instance)
    {
        if (instance == null) return;
        var cacheKey = ResolveUnitBase.GetCacheKey(instance.GetType(), null);
        var tempInstance = instance as IocResponse;
        if (tempInstance == null) tempInstance = new IocResponse(cacheKey, instance, ServiceLifetime.Scoped);
        addToResolvedResponse(tempInstance);
    }
    /// <summary>
    /// 注册可释放实例
    /// <para>不会被获取</para>
    /// </summary>
    public void RegisterForDispose(object dispose)
    {
        if (dispose == null) return;
        disposables.Add(dispose);
    }
    /// <summary>
    /// The <see cref="System.IServiceProvider"/> used to resolve dependencies from the scope.
    /// </summary>
    IServiceProvider IServiceScope.ServiceProvider => this;
    public object GetRequiredService(Type serviceType)
    {
        var result = Resolve(serviceType, null);
        if (result == null) throw new Exception($"类型 {EmitHelper.GetFullName(serviceType)} 尚未注入");
        return result;
    }
    public object? GetService(Type serviceType)
    {
        return Resolve(serviceType, null);
    }

    public T? Resolve<T>()
    {
        return Resolve<T>(null);
    }

    public T? Resolve<T>(string keyName)
    {
        return (T)Resolve(typeof(T), keyName);
    }
    bool isHttpInited;
    HttpContext _context;
    HttpContext httpContext
    {
        get
        {
            if (!isHttpInited)
            {
                isHttpInited = true;
                var cacheKey = ResolveUnitBase.GetCacheKey(typeof(IHttpContextAccessor), null);
                var httpInstance = GetFromResolved(cacheKey);
                if (httpInstance != null)
                {
                    _context = ((IHttpContextAccessor)httpInstance.Instance)?.HttpContext;
                }
            }
            return _context;
        }
    }
    void addToResolvedResponse(IocResponse response)
    {
        switch(response.ServiceLifetime)
        {
            case ServiceLifetime.Transient:
                disposables.Add(response.Instance);
                break;
            case ServiceLifetime.Singleton:
                if (rootIoc == null)
                {
                    _addToResolvedResponse(response);
                }
                else rootIoc._addToResolvedResponse(response);
                break;
            case ServiceLifetime.Scoped:
                _addToResolvedResponse(response);
                break;
            default:
                break;
        }
    }
    void _addToResolvedResponse(IocResponse response)
    {
        lock (lockObj)
        {
            responseds.Add(response);
        }
    }
    /// <summary>
    /// 从已获取的实例中获取
    /// <para>排除瞬态生命周期类型</para>
    /// </summary>
    /// <param name="cacheKey"></param>
    /// <returns></returns>
    internal IocResponse? GetFromResolved(string cacheKey)
    {
        IocResponse result;
        lock (lockObj)
        {
            result = responseds.FirstOrDefault(x => x.ServiceLifetime != ServiceLifetime.Transient && x.CacheKey == cacheKey);
        }
        if (result == null && rootIoc != null) result = rootIoc.GetFromResolved(cacheKey);
        return result;
    }
    internal IocResponse GetResponsedsFirstOrDefault(Func<IocResponse, bool> selector)
    {
        IocResponse? result;
        lock (lockObj)
        {
            result = responseds.FirstOrDefault(predicate: selector);
        }
        return result;
    }
    internal IocResponse[] GetResponsedsArray(Func<IocResponse, bool> selector)
    {
        IocResponse[]? result;
        lock (lockObj)
        {
            result = responseds.Where(predicate: selector).ToArray();
        }
        return result;
    }
    const string IServiceProviderKey = "System.IServiceProvider";
    public object? Resolve(Type serviceType, string keyName)
    {
        var serviceKeyName = ResolveUnitBase.GetCacheKey(serviceType);
        if (serviceKeyName == null || serviceKeyName.Length < 1) return null;
        var respone = resolveIocResponse(serviceType, serviceKeyName, keyName);
        return respone?.Instance;
    }
    internal IocResponse? ResolveIocResponse(Type serviceType, string keyName)
    {
        var serviceKeyName = ResolveUnitBase.GetCacheKey(serviceType);
        return resolveIocResponse(serviceType, serviceKeyName, keyName);
    }
    IocResponse? resolveIocResponse(Type serviceType, string serviceKeyName, string keyName)
    {
        if (_isDisposed) throw new ObjectDisposedException(nameof(LifeTime), "无法访问已释放容器");
        var cacheKey = ResolveUnitBase.GetCacheKey(serviceKeyName, keyName);
        var response = GetFromResolved(cacheKey);
        if (response != null)
        {
            return response;
        }
        var matchResolveUnit = resolveInterfaces.FirstOrDefault(x => x.EqualServiceKey(serviceKeyName));
        if (matchResolveUnit != null) response = matchResolveUnit.ResolveInstance(this, keyName);
        if (response == null)
        {
            if (!serviceType.IsGenericType) return null;
            var argumentTypes = serviceType.GetGenericArguments();
            var dType = serviceType.GetGenericTypeDefinition();
            serviceKeyName = ResolveUnitBase.GetCacheKey(dType);
            matchResolveUnit = resolveInterfaces.FirstOrDefault(x => x.EqualServiceKey(serviceKeyName));
            if (matchResolveUnit == null) return null;
            response = matchResolveUnit.ResolveInstance(this, argumentTypes, keyName);
            if (response == null) return null;
        }
        addToResolvedResponse(response);
        return response;
    }
    internal static MethodInfo GetResolveIocResponseMethod() => typeof(LifeTime).GetMethod(nameof(ResolveIocResponse), BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Type), typeof(string) }, null);
    internal static MethodInfo GetResolveIocResponseEnumerableMethod() => typeof(LifeTime).GetMethod(nameof(ResolveIocResponseEnumerable), BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
    /// <summary>
    /// 用于Emit方法调用
    /// </summary>
    /// <typeparam name="TService"></typeparam>
    /// <returns></returns>
    internal object? ResolveIocResponseEnumerable<TService>(string resultCacheKey)
    {
        if (_isDisposed) throw new ObjectDisposedException(nameof(LifeTime), "无法访问已释放容器");
        var serviceKeyName = EmitHelper.GetFullName(typeof(TService));
        var elementServiceKey = ResolveUnitBase.GetCacheKey(typeof(TService));
        var response = GetResponsedsFirstOrDefault(x => x.ServiceLifetime != ServiceLifetime.Transient && x.CacheKey == resultCacheKey);
        if (response != null)
        {
            return response.Instance;
        }
        if (rootIoc != null)
        {
            response = rootIoc.GetResponsedsFirstOrDefault(x => x.ServiceLifetime != ServiceLifetime.Transient && x.CacheKey == resultCacheKey);
            if (response != null)
            {
                return response.Instance;
            }
        }
        List<TService> services = new(2);
        var resolveds = GetResponsedsArray(x => x.ServiceLifetime != ServiceLifetime.Transient && x.CacheKey == elementServiceKey);
        if (resolveds != null)
        {
            foreach (var resolved in resolveds)
            {
                var tempObj = resolved.Instance;
                if (tempObj == null) continue;
                services.Add((TService)tempObj);
            }
        }
        if (rootIoc != null)
        {
            resolveds = rootIoc.GetResponsedsArray(x => x.ServiceLifetime != ServiceLifetime.Transient && x.CacheKey == elementServiceKey);
            if (resolveds != null)
            {
                foreach (var resolved in resolveds)
                {
                    var tempObj = resolved.Instance;
                    if (tempObj == null) continue;
                    services.Add((TService)tempObj);
                }
            }
        }
        var tempResolveUnit = resolveInterfaces.FirstOrDefault(x => x.EqualServiceKey(elementServiceKey));
        if (tempResolveUnit != null)
        {
            foreach (var tempResponse in tempResolveUnit.ResolveInstanceEnumerable(this, null))
            {
                var tempObj = tempResponse.Instance;
                if (tempObj == null) continue;
                addToResolvedResponse(tempResponse);
                services.Add((TService)tempObj);
            }
        }
        services.Reverse();
        return services.ToArray();
    }
    /// <summary>
    /// 析构函数
    /// </summary>
    ~LifeTime()
    {
        Dispose(false);
    }
    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Dispose(true);
    }
    bool _isDisposed;
    /// <summary>
    /// 释放资源
    /// </summary>
    /// <param name="isdisposing"></param>
    protected virtual void Dispose(bool isdisposing)
    {
        //Interlocked.MemoryBarrier();
        if (_isDisposed) return;
        _isDisposed = true;
        rootIoc = null;
        //从尾部开始释放
        for (var i = disposables.Count - 1; i >= 0; i--)
        {
            var instance = disposables[i];
            syncDispose(instance);
        }
        disposables.Clear();
        for (var i = responseds.Count - 1; i >= 0; i--)
        {
            var response = responseds[i];
            syncDispose(response.Instance);
        }
        responseds.Clear();
    }
    void syncDispose(object instance)
    {
        if (instance is IDisposable disposable)
        {
            disposable.Dispose();
        }
        else if (instance is IAsyncDisposable asyncDisposable)
        {
            Task.Run(async () => await asyncDisposable.DisposeAsync().ConfigureAwait(false))
                .ConfigureAwait(false)
                .GetAwaiter().GetResult();
        }
    }
    public ValueTask DisposeAsync()
    {
        GC.SuppressFinalize(this);
        return DisposeAsync(true);
    }
    /// <summary>
    /// 释放资源
    /// </summary>
    /// <param name="isdisposing"></param>
    protected virtual async ValueTask DisposeAsync(bool isdisposing)
    {
        //Interlocked.MemoryBarrier();
        if (_isDisposed) return;
        _isDisposed = true;
        rootIoc = null;
        //从尾部开始释放
        for (var i = disposables.Count - 1; i >= 0; i--)
        {
            var instance = disposables[i];
            await asyncDispose(instance);
        }
        disposables.Clear();
        for (var i = responseds.Count - 1; i >= 0; i--)
        {
            var response = responseds[i];
            await asyncDispose(response.Instance);
        }
        responseds.Clear();
    }
    async ValueTask asyncDispose(object instance)
    {
        if (instance is IAsyncDisposable asyncDisposable)
        {
            await asyncDisposable.DisposeAsync().ConfigureAwait(false);
        }
        else if (instance is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}